/**
 * dbms-sjtu
 * DbmsServer.java created at Aug 8, 2011
 */
package dbms.server;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import dbms.Constants;
import dbms.Debug;
import dbms.absyn.*;
import dbms.buffer.Buffer;
import dbms.buffer.BufferManager;
import dbms.disk.DiskManager;
import dbms.metadata.MasterPage;
import dbms.parse.Lexer;
import dbms.parse.parser;
import dbms.planner.QueryPlanner;
import dbms.planner.UpdatePlanner;
import dbms.query.PlanTree;
import dbms.query.ResultSet;

/**
 * @author weapon
 *
 */
public class DbmsServer {
	/**
	 * this is database file, only one 
	 */
	public static DiskManager dataBaseFile;
	
	/**
	 * the only one masterPage for sqlsjk_master table
	 */
	public static MasterPage masterPage;
	
	/**
	 * when user drop table, we put that table's root_page_number to this free list, 
	 * and delete this page when database exit.
	 */
	public static List<Integer> freePageList = new ArrayList<Integer>();
	
	
	public static BufferManager bufferManager;
	
	/**
	 * subquery we stores the resultset at here<br>
	 * key: view name or table name,   value: ResultSet
	 */
	public static Map<String, ResultSet> tempViewMap = new HashMap<String, ResultSet>();
	

	/**
	 * init the bufferManager and masterPage
	 */
	public static void init(String filename) {
		dataBaseFile = new DiskManager(filename);
		bufferManager = new BufferManager(Constants.BUFFER_POOL_LIMIT);
		Buffer master = bufferManager.allocBuffer();
		dataBaseFile.readPage(Constants.MASTER_BLOCK, master.page.contents);
		String version = master.getString(0, 16);
		if (version.equals("SQLsjk dbms v1.0")) {
			master.blockNum = Constants.MASTER_BLOCK;
			masterPage = new MasterPage(master, false);
		} else {
			masterPage = new MasterPage(master, true);
			masterPage.recordpage.buffer.page.write(Constants.MASTER_BLOCK);
		}
		master.connectToBlock(Constants.MASTER_BLOCK);
	}
	
	
	/**
	 * basic Dbms SQL executor wrap with function executeDbms()<br>
	 * use StringReader class to solve String sql
	 * @param sql : the SQL statement which will be execute
	 * @throws InterruptedException 
	 */
	public static String basicExecuteDbms(String sql) throws InterruptedException{
		Lexer lexer = new Lexer(new StringReader(sql));
		parser p = new parser(lexer);

		try {
			Object object = p.parse();
			if(object != null){
				ExpList sqlList = (ExpList)((java_cup.runtime.Symbol)object).value;
				return DbmsServer.executeDbms(sql, sqlList);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return null;
	}
	
	/**
	 * the Dbms SQL executor
	 * @param sqlList : sql tree
	 */
	public static String  executeDbms(String str, ExpList sqlList){
		while(sqlList != null){
			Exp sql = sqlList.exp;
			if(sql instanceof SelectExp){
				//SELECT ... FROM table WHERE ..
				return executeQuery((SelectExp)sql);
			} else if(sql instanceof InsertExp){
				//INSERT INTO table VALUES(...)
				UpdatePlanner.executeInsert((InsertExp)sql);
			} else if(sql instanceof UpdateExp){
				//UPDATE table SET ... WHERE ...
				UpdatePlanner.executeUpdate((UpdateExp)sql);
			} else if(sql instanceof DeleteExp){
				//DELETE FROM table WHERE ...
				UpdatePlanner.executeDelete((DeleteExp)sql);
			} else if(sql instanceof JoinExp){
				
			} else if(sql instanceof CreateDbClause){
				
			} else if(sql instanceof CreateTabClause){
				//CREATE TABLE table(id INT, name CHAR(10));
				UpdatePlanner.executeCreateTable((CreateTabClause)sql);
			} else if(sql instanceof UnionExp){
				
			} else if(sql instanceof UnionAllExp){
				
			} else if(sql instanceof AlterExp){
				
			} else if(sql instanceof CheckExp){
				
			} else if(sql instanceof IndexExp){
				
			} else if(sql instanceof DropExp){
				if(((DropExp)sql).drop_stmt instanceof DropTabClause){
					//DROP TABLE table;
					UpdatePlanner.executeDropTable(((DropTabClause)((DropExp)sql).drop_stmt));
				} else if(((DropExp)sql).drop_stmt instanceof DropViewClause){
					UpdatePlanner.executeDropView(((DropViewClause)((DropExp)sql).drop_stmt));
				} else {
					
				}
			} else if(sql instanceof ViewExp){
				UpdatePlanner.executeCreateView(str, (ViewExp)sql);
			} else{
				Debug.P("Illegal sql statement.");
			}
			sqlList = sqlList.next;
		}
		return null;
	}
	
	public static String executeQuery(SelectExp sql){
		String table = sql.from_stmt.tableList.attr.attr.name;
		if(DbmsServer.masterPage.viewMap.containsKey(table)){
			return QueryPlanner.executeQuery(sql);
		} else {
			PlanTree pTree = new PlanTree();
			pTree.query(sql);
			return pTree.execute().resultSet.toString();
		}
		
	}
	
	/**
	 * shutdown the database management system
	 */
	public static void shutdown(){
		//free page list
		masterPage.recordpage.buffer.setInt(20, MasterPage.freeListPageNum);
		bufferManager.flushAll();
		dataBaseFile.close();
		/**
		 * if user do not create any table, we destroy this file.
		 */
		if(masterPage.tableMap.isEmpty()){
			dataBaseFile.destroyFile(dataBaseFile.getFileName());
		}
	}
	
	
	public static void showHelp(){
		Debug.P("Enter the following commands to get more information: \n" +
				"help		get information about this\n" +
				"info		show information about database file\n" +
				"schema	(table name)		show this table's schema information\n" +
				"show tables		show all table name which stores in this database file\n" +
				"exit		shutdown the system.");
	}
}
